#include <iostream>
#include <string>
#include <cassert>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define MAX_LISTEN 1024
class Socket {
    private:
        int _sockfd;
    public:
        Socket::Socket():_sockfd(-1) {}
        Socket::Socket(int fd):_sockfd(fd) {}
        Socket::~Socket() { close(); }
        int Socket::fd() { return _sockfd; }
        void Socket::set_reuse() {
            int opt = 1;
            setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
            setsockopt(_sockfd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(int));
        }
        void Socket::set_nonblock() {
            int flag = fcntl(_sockfd, F_GETFL, 0);
            fcntl(_sockfd, F_SETFL, flag | O_NONBLOCK);
        }
        bool Socket::socket() {
            _sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            if (_sockfd < 0) {
                LOG("socket error");
                return false;
            }
            return true;
        }
        bool Socket::bind(const std::string &ip, int port) {
            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_port = htons(port);
            addr.sin_addr.s_addr = inet_addr(ip.c_str());
            socklen_t len = sizeof(struct sockaddr_in);
            int ret = ::bind(_sockfd, (struct sockaddr*)&addr, len);
            if (ret < 0) {
                LOG("bind error");
                return false;
            }
            return true;
        }
        bool Socket::connect(const std::string &ip, int port) {
            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_port = htons(port);
            addr.sin_addr.s_addr = inet_addr(ip.c_str());
            socklen_t len = sizeof(struct sockaddr_in);
            int ret = ::connect(_sockfd, (struct sockaddr*)&addr, len);
            if (ret < 0) {
                LOG("connect error");
                return false;
            }
            return true;
        }
        bool Socket::listen(int backlog = MAX_LISTEN) {
            int ret = ::listen(_sockfd, backlog);
            if (ret < 0) {
                LOG("listen error");
                return false;
            }
            return true;
        }
        int Socket::accept() {
            int newfd = ::accept(_sockfd, NULL, 0);
            if (newfd < 0) {
                LOG("accept error");
                return -1;
            }
            return newfd;
        }
        bool Socket::create_listener(int port, const std::string &ip = "0.0.0.0") {
            assert(socket());
            //set_nonblock();
            set_reuse();
            assert(bind(ip, port));
            assert(listen());
            return true;
        }
        bool Socket::create_client(const std::string &srvip, int srvport) {
            assert(socket());
            assert(connect(srvip, srvport));
            return true;
        }
        ssize_t Socket::recv(void *buf, size_t len, int flag = 0) {
            int ret = ::recv(_sockfd, buf, len, flag);
            if (ret <= 0) {
                if (errno == EAGAIN || errno == EINTR) {
                    return 0;
                }
                LOG("recv error or peer shutdown!");
                return -1;
            }
            return ret;
        }
        ssize_t Socket::nonblock_recv(void *buf, size_t len) { 
            return recv(buf, len, MSG_DONTWAIT); 
        }
        ssize_t Socket::send(const void *data, size_t len, int flag = 0) {
            int ret = ::send(_sockfd, data, len, flag);
            if (ret < 0) {
                if (errno == EAGAIN || errno == EINTR) {
                    return 0;
                }
                LOG("send error!");
                return -1;
            }
            return ret;
        }
        ssize_t Socket::nonblock_send(const void *buf, size_t len) { 
            return send(buf, len, MSG_DONTWAIT); 
        }
        void Socket::close() { 
            if (_sockfd >= 0) {
                LOG("close sockfd:%d", _sockfd);
                ::close(_sockfd); 
                _sockfd = -1; 
            } 
        }
};